

/***
 * @Date            : 2024-12-09 23:35:37
 * @Author          : AnimateX(Baron)
 * @LastEditors     : AnimateX(Baron)
 * @LastEditTime    : 2024-12-09 23:35:48
 * @FilePath        : /src/p16.cpp
 * @CopyRight       : Copyright (c) 2024 RockChip Technology Co. Ltd. All rights reserved.
 * @Description     :
 */
#include <cassert>
#include <cstddef>
#include <ios>
#include <string>
#include <string_view>
#include <vector>
#include <iostream>
#include <utility>
#include <limits>
#include "p16.h"


namespace CourseP16 {
    void test_p16_2() {
        std::vector primes { 2, 3, 5, 7, 11 };
        std::cout << "The first prime number is: " << primes[0] << '\n';
        std::cout << "The second prime number is: " << primes[1] << '\n';
        std::cout << "The sum of the first 5 primes is: " << primes[0] + primes[1] + primes[2] + primes[3] + primes[4] << '\n';
    }

    void homework_01() {
        std::vector squares{ 1, 4, 9, 16, 25 };
        std::cout << "Vector squares elements (range-based for): ";
        for (const auto& value : squares) {
            std::cout << value << " ";
        }
        std::cout << std::endl;
    }

    void homework_02() {
        std::vector<int> v1{ 5 };
        std::vector<int> v2( 5 );

        std::cout << "Vector v2 elements (range-based for): ";
        for (const auto& value : v2) {
            std::cout << value << " ";
        }
        std::cout << std::endl;

        std::cout << "Vector v1 elements (range-based for): ";
        for (const auto& value : v1) {
            std::cout << value << " ";
        }
        std::cout << std::endl;
    }

    void homework_03() {
        std::vector<double> temperature(365, 20.f);
        std::cout << "Vector temperature elements (range-based for): ";
        for (const auto& value : temperature) {
            std::cout << value << " ";
        }
        std::cout << std::endl;
    }

    void homework_04() {
        std::vector<int> inputVal(3, 0);
        std::cout << "Enter three integers: ";
        std::cin >> inputVal[0] >> inputVal[1] >> inputVal[2];

        std::cout << "The sum is: " << inputVal[0] + inputVal[1] + inputVal[2] << '\n';
        std::cout << "The product is: " << inputVal[0] * inputVal[1] * inputVal[2] << '\n';

    }

    void course_16_3() {
        std::vector prime { 2, 3, 4, 5, 7, 11 };
        std::cout << " [Info] length: " << std::ssize(prime) << '\n';

        auto length { std::ssize(prime) };
        std::cout << " [Info] length: " << length << '\n';

        // 通常我们使用operator[] 是不会边界检查的，但是at会
        std::cout << " [Info] elem prime[3]: " << prime.at(3) << '\n';
        // std::cout << " [Info] elem prime[3]: " << prime.at(9) << '\n'; // invalid index

        constexpr int index {3};
        std::cout << " [Info] Test constexpr index: " << prime[index] << '\n';

        int index_new {3};
        std::cout << " [Info] Test index: " << prime.data()[index_new] << '\n';
    }

    template <typename T>
    void passByRef(const std::vector<T>& arr) {
        std::cout << arr[0] << '\n';
    }

    void test_vector_pass_by_ref() {
        std::vector primes {2, 4, 5, 8, 12};
        passByRef(primes);

        std::vector str_test {'a', 'b', 'c', 'd', 'e'};
        passByRef(str_test);
    }

    template <typename T>
    void pasByRefNormal(const T& arr) {
        std::cout << arr[0] << '\n';
    }

    void test_pass_by_ref() {
        std::vector primes {2, 4, 5, 8, 12};
        pasByRefNormal(primes);

        std::vector str_test {'a', 'b', 'c', 'd', 'e'};
        pasByRefNormal(str_test);
    }

    template <typename T>
    void printElement3(const std::vector<T>& arr) {
        if (arr.size() > 3) {
            std::cout << arr[3] << '\n';
        } else {
            std::cout << arr[0] << '\n';
        }
    }

    void test_printElement3() {
        std::vector primes {2, 4};
        printElement3(primes);
    }

    void course_16_4() {
        test_vector_pass_by_ref();
        test_pass_by_ref();
        test_printElement3();
    }

    template <typename T>
    void printElement(std::vector<T>& arr, int index) {
        if (index < static_cast<int>(arr.size()) && index >= 0) {
            std::cout << "This element value: " << arr[index] << '\n';
        } else {
            std::cout << "Invalid index" << '\n';
        }
    }

    void homework_16_4() {
        std::vector v1 { 0, 1, 2, 3, 4 };
        printElement(v1, 2);
        printElement(v1, 5);

        std::vector v2 { 1.1, 2.2, 3.3 };
        printElement(v2, 0);
        printElement(v2, -1);
    }

    std::vector<int> generate() {
        std::vector arr1 { 1, 2, 3, 4, 5 };
        return arr1;
    }

    std::vector<int> doSomething(std::vector<int> v2) {
        std::vector v3 { v2[0] + v2[0] };
        return v3;
    }

    // Use std::move
    std::vector<int> doSomethingNew(std::vector<int>&& v2) {
        std::vector<int> v3 { v2[0] + v2[0] };
        // return std::move(v3);
        return v3;
    }


    void course_16_5() {
        // std::vector arr2 { generate() };
        // arr2[0] = 7;

        // std::cout << arr2[0] << '\n';

        std::vector v1 { 5 };
        std::cout << doSomething(v1)[0] << '\n';
        std::cout << v1[0] << '\n';

        std::cout << doSomethingNew(std::move(v1))[0] << '\n';
        std::cout << v1[0] << '\n';

        std::cout << "v1 size after move: " << v1.size() << '\n';  // 可能是 0 或大于 0
        std::cout << "v1 content after move: ";
        for (auto& val : v1) {
            std::cout << val << " ";
        }
        std::cout << '\n';
    }

    template<typename T>
    void print_vector(const std::vector<T>&vec, const std::string& name) {
        std::cout << " [Info] " << name << " ( [Address]: " << &vec << ", [Size]: " << vec.size() <<" ): ";
        for (const auto& val : vec) {
            std:: cout << val << " ";
        }
        std::cout << std::endl;
    }

    std::vector<int> testVectorMove(std::vector<int>&& vec) {
        std::cout << " [Info] (before move) vec ( [Address]: " << &vec << " ): ";
        for (const auto& val : vec) {
            std::cout << val << " ";
        }
        std::cout << std::endl;

        // move vec to vecNew
        std::vector<int> vecNew = std::move(vec);
        std::cout << " [Info] (After move) vec ( [Address]: " << &vec << " ): ";
        for (const auto& val : vec) {
            std::cout << val << " ";
        }
        std::cout << std::endl;

        std::cout << " [Info] vecNew ( [Address]: " << &vecNew << " ): ";
        for (const auto& val : vecNew) {
            std::cout << val << " ";
        }
        std::cout << std::endl;

        return vecNew;
    }

    void course_16_5_add() {
        std::vector<int> v1 { 1, 2 };
        std::cout << " [Info] Begin calling testVectorMove: " << std::endl;
        print_vector(v1, "v1");

        // move v1 to testVectorMove
        std::vector<int> result = testVectorMove(std::move(v1));

        std::cout << " [Info] After calling testVectorMove: " << std::endl;
        print_vector(result, "result");

        std::cout << " [Info] After calling testVectorMove, v1 info: " << std::endl;
        std::cout << " [Info] v1 [Address]: " << &v1 << " Size: " << v1.size() << std::endl;

        if (!v1.empty()) {
            std::cout << " [Info] v1 content (After move): ";
            for (const auto& val: v1) {
                std::cout << val << " ";
            }
            std::cout << std::endl;
        } else {
            std::cout << " [Error] v1 is empty (After move)!" << std::endl;
        }
    }

    void homework_16_6_1() {
        std::vector arr { 4, 6, 7, 3, 8, 2, 1, 9 };

        if (!arr.empty()) {
            for (auto val : arr) {
                std:: cout << val << " ";
            }
            if (arr.size() > 0) {
                std:: cout << '\n';
            }
        } else {
            std::cout << " [Error] arr is empty!" << '\n';
        }
    }

    template<typename T>
    void printArray(const std::vector<T>& arr) {
        if (!arr.empty()) {
            for (auto val : arr) {
                std:: cout << val << " ";
            }
            if (arr.size() > 0) {
                std:: cout << '\n';
            }
        } else {
            std::cout << " [Error] arr is empty!" << '\n';
        }
    }

    void homework_16_6_2() {
        std::vector arr { 4, 6, 7, 3, 8, 2, 1, 9 };
        printArray(arr);
    }

    bool hasUnextractedInput() {
        return !std::cin.eof() && std::cin.peek() != '\n';
    }

    void ignoreLine() {
        std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    }

    template <typename T>
    T getInputNum(std::string_view prompt, T low, T high) {
        while (true) {
            std::cout << " [Info] " << prompt << " between " << low << " and " << high << ": ";
            T x {};
            std::cin >> x;
#if 0
            if (hasUnextractedInput()) {
                ignoreLine();
                continue;
            }
#endif
            if (std::cin.fail()) {
                std::cin.clear();
                ignoreLine();
                std::cout << " [Error] Invalid input! Please enter a valid number" << " between " << low << " and " << high << "\n";
                continue;
            }

            if (x >= low && x <= high) {
                return x;
            } else {
                std::cout << " [Warning] Out of range! Please enter a number between 1 and 9.\n";
                continue;
            }
        }
    }

    template<typename T>
    int isElemInVector(std::vector<T>& arr, const T& elem) {
        if (!arr.empty()) {
            for (size_t i = 0; i < arr.size(); ++i) {
                if (elem == arr[i]) {
                    return static_cast<int>(i);
                }
            }
        }
        return -1;
    }

    template<typename T>
    T findMax(std::vector<T>& arr) {
        if (!arr.empty()) {
            T maxVal = arr[0];
            for (auto val : arr) {
                if (val > maxVal) {
                    maxVal = val;
                }
            }
            return maxVal;
        } else {
            return 0;
        }
    }

    void homework_16_6_3() {
        std::vector arr { 4, 6, 7, 3, 8, 2, 1, 9 };
        int x = getInputNum("Enter a number", 1, 9);
        printArray(arr);
        int idx = isElemInVector(arr, x);
        if (idx != -1) {
            std::cout << " [Info] The number " << x << " has index " << idx << '\n';
        }
    }

    void homework_16_6_4() {
        std::vector arr { 4.4, 6.6, 7.7, 3.3, 8.8, 2.2, 1.1, 9.9  };
        double x = getInputNum("Enter a number", 1.0, 9.0);
        printArray(arr);
        int idx = isElemInVector(arr, x);
        if (idx != -1) {
            std::cout << " [Info] The number " << x << " has index " << idx << '\n';
        }
    }

    void homework_16_6_5() {
        std::vector data1 { 84, 92, 76, 81, 56 };
        std::cout << findMax(data1) << '\n';

        std::vector data2 { -13.0, -26.7, -105.5, -14.8 };
        std::cout << findMax(data2) << '\n';

        std::vector<int> data3 { };
        std::cout << findMax(data3) << '\n';
    }

    bool printResult(const int num, const std::vector <int>& arr, const std::vector <std::string_view>& key_strs) {
        assert(arr.size() == key_strs.size() && " [Error] Check special array and key strings, size not match.\n");

        bool state = false;

        for (size_t idx = 0; idx < arr.size(); ++idx) {
            if (num % arr[idx] == 0) {
                std::cout << key_strs[idx] << " ";
                state = true;
            }
        }

        if (state) {
            std::cout << '\n';
        }

        return state;
    }

    void homework_16_6_6() {
        std::vector<int> divisors { 3, 5, 7, 11, 13, 17, 19 };
        std::vector<std::string_view> key_strs {
            "fizz", "buzz", "pop", "bang", "jazz", "pow", "boom"
        };

        int x = getInputNum("Enter a number", 0, 1000);
        printResult(x, divisors, key_strs);

        for (int i {1}; i < 150; ++i) {
            bool state = printResult(i, divisors, key_strs);
            if (!state) {
                std::cout << i << '\n';
            }
        }
    }

    void homework_16_6() {
        std::cout << "\n [Info] Test homework chapter-16-6-1\n";
        homework_16_6_1();
        std::cout << "\n [Info] Test homework chapter-16-6-2\n";
        homework_16_6_2();
        std::cout << "\n [Info] Test homework chapter-16-6-3\n";
        homework_16_6_3();
        std::cout << "\n [Info] Test homework chapter-16-6-4\n";
        homework_16_6_4();
        std::cout << "\n [Info] Test homework chapter-16-6-5\n";
        homework_16_6_5();
        std::cout << "\n [Info] Test homework chapter-16-6-6\n";
        homework_16_6_6();
    }

    // 不理想，两个强制转换让代码变的混乱
    template<typename T>
    void printReverse(const std::vector<T>& arr) {
        for (int index {static_cast<int>(arr.size() - 1)}; index >= 0; --index) {
            std::cout << arr[static_cast<std::size_t>(index)] << ' ';
        }
        std::cout << '\n';
    }

    // 同样的问题如下：
    template<typename T>
    T calculateAverage(const std::vector<T>& arr) {
        int length { static_cast<int>(arr.size()) };

        T average { 0 };

        for (int index { 0 }; index < length; ++index) {
            average += arr[static_cast<std::size_t>(index)];
        }
        average /= length;

        return average;
    }


    template<typename T>
    void printArrayNew(const std::vector<T>& arr) {
        // typename keyword prefix required for dependent type
        for (typename std::vector<T>::size_type index {0}; index < arr.size(); ++index) {
            std::cout << arr[index] << " ";
        }
        std::cout << std::endl;

        // decltype 无法针对引用，所以这里要先解引用
        for (typename std::remove_reference_t<decltype(arr)>::size_type index {0}; index < arr.size(); ++index) {
            std::cout << arr[index] << " ";
        }
        std::cout << std::endl;

        // 常见用法
        for (std::size_t index {0}; index < arr.size(); ++index) {
            std::cout << arr[index] << " ";
        }
        std::cout << std::endl;

        for (std::ptrdiff_t index {0}; index < static_cast<std::ptrdiff_t>(arr.size()); ++index) {
            std::cout << arr[index] << " ";
        }
        std::cout << std::endl;
    }

    void course_16_7() {
        std::vector arr { 9, 7, 5, 3, 1 };
        auto length {static_cast<Index>(arr.size()) };

        for (auto index {length - 1}; index >= 0; --index) {
            std::cout << arr.data()[index] << ' ';
        }
        std::cout << '\n';

        SignedArrayView view_arr {arr};
        for (auto index {view_arr.ssize()}; index >= 0; --index) {
            std::cout << view_arr[index] << ' ';
        }
        std::cout << '\n';
    }

    void test_iterate_container() {
        std::vector <std::string> words { "peter", "likes", "frozen", "yogurt" };

        // low mem cost, use const ref + auto
        for (const auto& word : words) {
            std::cout << word << ' ';
        }
        std::cout << '\n';

        // best method
        std::vector <std::string_view> words2 { "peter", "likes", "frozen", "yogurt" };

        // low mem cost, use const ref + auto
        for (const auto& word : words2) {
            std::cout << word << ' ';
        }
        std::cout << '\n';
    }

    template <typename T>
    T getInputName() {
        T name {};

        while (true) {
            std::cout << "Enter a name: ";
            std::cin >> name;

            if (std::cin.fail()) {
                std::cin.clear();
                ignoreLine();
                std::cout << "\n [Error] Please input new name(string): ";
                continue;
            }
            break;
        }

        return name;
    }

    void homework_16_8_1() {
        std::vector <std::string_view> words {
            "Alex", "Betty", "Caroline", "Dave", "Emily", "Fred", "Greg", "Holly"
        };

        std::string name = getInputName<std::string>();

        bool state = false;
        for (const auto& word : words) {
            if (word == name) {
                state = true;
            }
        }

        if (state) {
            std::cout << name << " was found.\n";
        } else {
            std::cout << name << " was not found.\n";
        }
    }

    template<typename T>
    bool isValueInArray(std::vector<T>& arr, const T& value) {
        for (const auto& elem : arr) {
            if (value == elem) {
                return true;
            }
        }

        return false;
    }

    void homework_16_8_2() {
        std::vector<std::string_view> names {
            "Alex", "Betty", "Caroline",
            "Dave", "Emily", "Fred",
            "Greg", "Holly"
        };
        std::string name = getInputName<std::string>();

        bool state { isValueInArray<std::string_view>(names, name) };
        if (state) {
            std::cout << name << " was found.\n";
        } else {
            std::cout << name << " was not found.\n";
        }
    }

}
